package multi_thread.master.lock_aqs;

import multi_thread.advanced.COWExample;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author Cheng Jun
 * Description: 读写锁
 * 读读共享，读写互斥，写写互斥；只要有写就会互斥。适合 CachedData
 * ReentrantReadWriteLock的缺点：如果读取执行情况很多，写入很少的情况下，使用 ReentrantReadWriteLock（非公平锁）
 * 可能会使写入线程遭遇饥饿（Starvation）问题，也就是写入线程迟迟无法竞争到锁定而一直处于等待状态。
 * 优点是相对于 COW 容器 更高效和方便
 * @version 1.0
 * @date 2022/5/18 13:35
 * @see ReentrantReadWriteLock
 * @see COWExample
 * @see StampedLockExample
 * 思考：COW 容器适合 读多写少的场景，为什么 juc 提供 ReentrantReadWriteLock ?
 * COW容器适合读多写少，但是它不能满足实时一致性要求，同时它在数据量大时，写时复制很占内存
 * 思考: ReentrantReadWriteLock 相比于 ReentrantLock 的优势？
 * 读读共享
 */
public class ReentrantReadWriteLockExample {
    // 成为读锁时，只增加state计数，而exclusiveOwner处依然指向空。
    // 成为写锁时，exclusiveOwner指向该线程。
    public static void main(String[] args) {
        // 读写锁，默认非公平锁，也可以设置成公平锁
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        // 读锁
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
        // 写锁
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
        ReadClass readClass0 = new ReadClass(readLock);
        ReadClass readClass1 = new ReadClass(readLock);

        WriteClass writeClass0 = new WriteClass(writeLock);
        WriteClass writeClass1 = new WriteClass(writeLock);

        readClass0.start();
        // readClass1.start();
        writeClass0.start();
        // writeClass1.start();

    }

    static class ReadClass extends Thread {
        private ReentrantReadWriteLock.ReadLock readLock;

        @Override
        public void run() {
            try {
                // 看看源码的加锁注释 java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryAcquireShared
                readLock.lock();
                System.out.println(Thread.currentThread().getName()+ "在" + LocalDateTime.now() + " 获取读锁");
                // 过两秒再释放读锁
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        }

        ReadClass(ReentrantReadWriteLock.ReadLock readLock) {
            this.readLock = readLock;
        }
    }

    static class WriteClass extends Thread {
        private ReentrantReadWriteLock.WriteLock writeLock;

        @Override
        public void run() {
            try {
                // 看看源码的加锁注释 java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryAcquire
                writeLock.lock();
                System.out.println(Thread.currentThread().getName() + "在"+ LocalDateTime.now() +" 获取写锁");
                // 过两秒再释放读锁
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        }

        WriteClass(ReentrantReadWriteLock.WriteLock writeLock) {
            this.writeLock = writeLock;
        }
    }
}
